﻿using Core.Dtos;
using Core.Domain;
using Core.Factories;
using Core.IGateways;
using Core.IRepository;
using Core.IServices;
using Core.Messages;
using Core.Queries;
using Core.Workflows;
using Core.Mappers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Core.Aspects;
using Core.Context;
using Core.IRepository.Office;

namespace Services
{
    [CacheName("Policy")]
    public class PolicyService : BaseService, IManageCrudService<int, PolicyDto, PolicyQuery>, IPolicyService
    {        
        private readonly IPolicyRepository policyRepository;
        private readonly IOfficeRepository<int, Person> personRepository;
        private readonly IPolicyReport policyReport;
        private readonly IWorkflowFactory workflowFactory;        
        //private readonly IAppContext appDictionaries;
        //private readonly IAppSettings appSettings;
        
        private readonly IUnitOfWork unitOfWork;
        private readonly IGenericReaderService<int, Policy, PolicyDto, PolicyQuery> readerService;
        private readonly IGenericWriterService<int, Policy, PolicyDto, PolicyQuery> writerService;

        public PolicyService(IOfficeUnitOfWork unitOfWork, IPolicyRepository policyRepository, IOfficeRepository<int, Person> personRepository, IPolicyReport policyReport, IWorkflowFactory workflowFactory, IGenericReaderService<int, Policy, PolicyDto, PolicyQuery> readerService, IGenericWriterService<int, Policy, PolicyDto, PolicyQuery> writerService)
        {
            this.policyRepository = policyRepository;
            this.personRepository = personRepository;
            this.policyReport = policyReport;        
            this.workflowFactory = workflowFactory;            
            
            this.unitOfWork = unitOfWork;
            this.readerService = readerService;
            this.writerService = writerService;
            readerService.Initialize(unitOfWork, policyRepository);
            writerService.Initialize(unitOfWork, policyRepository);
        }

        [Cache]
        public PolicyDto FindByID(int id)
        {
            var policy = FindByIdWithWorkflowInstantiated(id);
            return policy.Map<PolicyDto>();
        }

        public QueryResponse<PolicyDto> FindBy(Request<PolicyQuery> request)
        {
            //request.Value.UserId = request.Context.UserId;
            //if(request.Context.IsInRole("Supervisor"))                            
            //    request.Value.UserId = null;

            return readerService.FindBy(request);
        }

        private Policy FindByIdWithWorkflowInstantiated(int id)
        {
            Policy policy = policyRepository.FindBy(id);
            policy.InitializeDependencies(workflowFactory);
            policy.InstantiateWorkflow();
            return policy;                                    
        }

        public Response<int> Add(Request<PolicyDto> request)
        {
            PolicyDto policyDto = request.Value;
            policyDto.UserID = request.Context.UserId;            
            policyDto.DateCreated = DateTime.Now;

            return writerService.Add(policyDto, (policy) => {
                policy.InitializeDependencies(workflowFactory);
                policy.SetInPending();
                SetPersonFromDto(policy, policyDto);
                return policy;
            });
        }

        [DeleteCache]
        public Response Edit(int id, Request<PolicyDto> request)
        {
            PolicyDto policyDto = request.Value;
            return writerService.Edit(id, policyDto, policy => SetPersonFromDto(policy, policyDto));
        }

        private Policy SetPersonFromDto(Policy entity, PolicyDto dto)
        {
            string personName = dto.Person.Name;

            Person person = personRepository.FindBy(x => x.Name.Equals(personName)).FirstOrDefault();
            if (person != null)
            {
                entity.Person = person;
                entity.PersonID = person.Id;
            }
            else
            {
                person = dto.Person.Map<Person>();
                personRepository.Add(person);
                entity.Person = person;
                entity.PersonID = person.Id;
            }

            return entity;
        }

        [DeleteCache]
        public Response Delete(int id)
        {
            return writerService.Delete(id);
        }

        [Transaction]
        [DeleteCache]
        [HandleServiceError]
        public Response<PolicyDto> DoAction(int id, DoActionRequest request)
        {
            var response = new Response<PolicyDto>();

            var policy = FindByIdWithWorkflowInstantiated(id);
            policy.DoAction(request.Action);
            unitOfWork.Commit();

            response.Value = policy.Map<PolicyDto>();
            response.Success = true;
            return response;
        }
       
        public Stream BuildReport(int id)
        {
            string reportPath = HttpContext.Current.Server.MapPath("/");

            Policy policy = policyRepository.FindBy(Convert.ToInt32(id));
            Hashtable parameters = new Hashtable();
            parameters["policy"] = policy.Map<PolicyDto>();

            Stream fileStream = policyReport.GetPdf(reportPath, parameters);
            return fileStream;
        }
    }
}